"""
  Copyright (c) 2024 Northeastern University (China)
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
"""
# 目标：实现对已经识别出的结果自动化验证
# 验证标准:
# 1.重用路径中是否有oss名称
# 2.目标代码使否包含oss.h
# 3.目标代码是否包含oss的顶层配置文件
# 返回结果：之前的分析报告不改变，新增加verify:表示从哪里看出组件是目标代码成分的线索
# reliability：表示组件是目标代码成分的可信度

import difflib
import os
import traceback

from src.common_utils.utils_file import read_jl, write_line_jl
from src.operate_db.Component import Component as Component


class Verification:
    component=Component()
    similar_theta = 0.9
    def find_short_path(self, start_directory, target_short_path):
        for root, _, files in os.walk(start_directory):
            for file in files:
                full_path = os.path.join(root, file).lower()  # 全部换成小写，因为组件名称在一开始就被换成小写了
                # 检查是否包含目标短路径
                if target_short_path in full_path:
                    return os.path.relpath(full_path,start_directory)
        return None

    def check_h_file(self, target_src_path, target_head_file):
        for root, dirs, files in os.walk(target_src_path):
            for file in files:
                if file.endswith(".h"):
                    matcher_headfile = difflib.SequenceMatcher(None, file.lower(), target_head_file)
                    similarity_ratio_headfile = matcher_headfile.ratio()
                    if similarity_ratio_headfile >= self.similar_theta:
                        return os.path.relpath(os.path.join(root, file),target_src_path)
        return None
    def analysis_common(self,report,target_src_path):
        """
        report:组件识别的检测结果，是个dict列表，对应每个组件的具体信息
        target_src_path:目标代码的路径
        """
        # 开始验证结果正确性
        for data in report:
            reliability=0 #表示这个组件是目标代码的成员的可信度，默认是0
            try:
                report_name, report_url, report_version, report_paths,origin_func_cnt,feature_func_cnt,used_same,used_modified,used_strChange,unused = data.values()
                data['verify'] = []
                reponame = report_url.split('/')[-1]  # 取url最后一个节点作为名字
                break_flag = False
                for path in report_paths:
                    path_node = path.lower().split("\\")#注意：先变成小写，因为组件的名称一开始就变成小写了
                    for node in path_node:
                        #1. 路径中是否包含oss名称
                        node=node.replace(".cc","")
                        node = node.replace(".cpp", "")
                        node = node.replace(".c", "")
                        matcher = difflib.SequenceMatcher(None, node, reponame)
                        similarity_ratio = matcher.ratio()
                        if similarity_ratio >= self.similar_theta:  # 正确检测
                            print(f"从函数路径检测到正确{report_name}:{path}")
                            reliability+=2   #重用路径中包含oss名称认为权重是2
                            data['verify'].append((path,"path contain oss name"))
                            break_flag = True
                            break
                    if break_flag:
                        break
                #所有的路径都检查了一遍，发现第一种情况失败,或者第一种情况成功，都会执行第二种方式
                # 2.查看目标源代码中是否包含oss.h
                target_head_file = reponame + ".h"
                head_file_res = self.check_h_file(target_src_path, target_head_file)
                if head_file_res is not None:
                    print(f"检测到目标源码中有组件{report_name}名称的头文件{target_head_file}")
                    reliability += 2  # 认为目标代码包含oss.h的权重是2
                    data['verify'] .append( (head_file_res,"has ossname.h"))
                #3.要看目标项目中是否包含oss的顶层配置文件,先到组件的源码处找到配置文件
                break_iner_flag = False
                metadata_file = self.component.get_data({'sig_full_name':report_name},{'metafile':1})
                if len(metadata_file)!=0:
                    metadata_file=metadata_file[0]
                    metadata_path = [os.path.join(reponame, file) for file in metadata_file['metafile']]
                    for path in metadata_path:
                        res = self.find_short_path(target_src_path, path.lower())
                        if res is not None:
                            print(f"从顶层配置文件检测{report_name} 正确：{res}")
                            reliability += 1  # 认为目标代码包含oss配置文件的权重是1,因为具体是不是还要看内容
                            data['verify'].append((res,'has oss metafile'))
                            break_iner_flag = True
                            break
                if len(data['verify'])==0:  # 说明已经将三个标准全部验证了一遍，没有找到目标代码存在这个组件的证据
                    print(f"组件{report_name}自动化验证认为是多余的，需要人工分析")
                    data['verify'].append((None, 'require manual analysis'))
            except Exception as e:
                print("Parser Error:", e)
                traceback.print_exc()
                continue
            data['reliability']=reliability
        return report


if __name__ == "__main__":
    verification=Verification()
    resultPath=input("resultPath:")
    target_src_path=input("target_src_path:")
    res = read_jl(os.path.join(resultPath))  # 读取检测报告
    if len(res) != 0:
        print(f"读取检测报告中的检测组件个数:{len(res)}")
        analysis_detect = verification.analysis_common(res, target_src_path)
        print(len(analysis_detect))
        write_line_jl(os.path.join(r"E:\open_harmony_src\src\component_identification\data_out",  "verify.jl"), analysis_detect)